#include "CloudiskServer.h"
#include "CryptoUtil.h"
#include "CloudMoniter.h"
#include <iostream>
#include <vector>
#include <signal.h>

#include "wfrest/PathUtil.h"
#include "workflow/MySQLResult.h"
#include "nlohmann/json.hpp"

using namespace std;
using namespace wfrest;
using namespace protocol;
using json = nlohmann::json;

UserInfo user;
FileInfo file;
void CloudiskServer::register_modules()
{
    // 设置静态资源的路由
    register_static_resources_module();
    register_signup_module();
    register_signin_module();
    register_userinfo_module();
    register_filelist_module();
    register_fileupload_module();
    register_filedownload_module();
}

void CloudiskServer::register_static_resources_module()
{
    m_server.GET("/user/signup", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/signup.html");
    });

    m_server.GET("/static/view/signin.html", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/signin.html");
    });

    m_server.GET("/static/view/home.html", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/home.html");
    });

    m_server.GET("/static/js/auth.js", [](const HttpReq *, HttpResp * resp){
        resp->File("static/js/auth.js");
    });

    m_server.GET("/static/img/avatar.jpeg", [](const HttpReq *, HttpResp * resp){
        resp->File("static/img/avatar.jpeg");
    });

    m_server.GET("/file/upload", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/index.html");
    });

    m_server.Static("/file/upload_files","static/view/upload_files");
}

void CloudiskServer::register_signup_module()
{
    m_server.POST("/user/signup",[](const HttpReq* req,HttpResp* resp){
        if( req->content_type() != APPLICATION_URLENCODED )
        {
            resp->set_status(HttpStatusBadRequest);
            resp->String("<html>400 Bad Request!</html>");
            return;
        }

        map<string,string> dataForm=req->form_kv();
        user.username = dataForm["username"];
        user.password = dataForm["password"];

        srand(time(NULL));
        string salt = CryptoUtil::generate_salt(8);
        string hash = CryptoUtil::generate_hash(user.password,salt);

        string sql="INSERT INTO tbl_user (username,password,salt) VALUES('" + user.username 
        + "','" + hash + "','" + salt + "');";
        #if DEBUG
        cout << "username= " << user.username << endl;
        cout << "password= " << user.password << endl;
        cout << "salt= " << salt << endl;
        cout << "hash= " << hash << endl;
        cout << "sql= " << sql << endl;
        #endif 
        resp->MySQL("mysql://root:123@localhost/test",sql,[resp](MySQLResultCursor* cursor){
            if( cursor->get_cursor_status() == MYSQL_STATUS_OK )
            {
                resp->String("SUCCESS");
            }
            else
            {
                resp->set_status(HttpStatusBadRequest);
                resp->String("注册失败，用户名可能已存在！\n");
            }
        });
    });
}

void CloudiskServer::register_signin_module()
{
    m_server.POST("/user/signin",[](const HttpReq* req,HttpResp* resp){
        if( req->content_type() != APPLICATION_URLENCODED )
        {
            resp->set_status(HttpStatusBadRequest);
            resp->String("<html>400 Bad Request!</html>");
            return;
        }

        auto &params = req->form_kv();
        user.username = params["username"];
        user.password = params["password"];
        string sql = "SELECT id,salt,password FROM tbl_user where username = '" + 
        user.username + "';";
        #if DEBUG
        cout << "username= " << user.username << endl;
        cout << "password= " << user.password << endl;
        cout << "Sql= " << sql << endl;
        #endif
        resp->MySQL("mysql://root:123@localhost/test",sql,[resp](MySQLResultCursor* cursor){
            if( cursor->get_cursor_status() != MYSQL_STATUS_GET_RESULT )
            {
                resp->set_status( HttpStatusBadRequest );
                resp->String("登录失败，用户名或密码错误！\n");
                return;
            }

                vector<MySQLCell> record;
                string hash,salt;
                if( cursor->fetch_row( record ) )
                {   
                    user.id = (long)record[0].as_int();
                    salt = record[1].as_string();
                    hash = record[2].as_string();
                }
                string generateHash=CryptoUtil::generate_hash(user.password,salt);
                #if DEBUG
                cout << "salt= " << salt << endl;
                cout << "hash= " << hash << endl;
                cout << "user.id= " << user.id << endl;
                cout << generateHash << endl;
                #endif

                string token;
                if( hash != generateHash )
                {
                    resp->set_status( HttpStatusBadRequest );
                    resp->String("登录失败，用户名或密码错误！\n");
                    return;
                }
                else
                {
                    token = CryptoUtil::generate_token(user);
                    json result = {{
                    "data", {
                                {"Username",user.username},
                                {"Token",token},
                                {"Location","/static/view/home.html"}
                            }
                    }};

                    resp->String( result.dump() );
                    #if DEBUG
                    cout << "token= " << token << endl;
                    for(auto &[keyCon,valueCon] : result.items())
                    {
                        cout << "keyCon= " << keyCon << endl;
                        cout <<  "username= " << valueCon["Username"] << endl;
                        cout << "token= " << valueCon["Token"] << endl;
                        cout << "Location= " << valueCon["Location"] << endl;
                    }
                    #endif
                }
        });
    });
}

void CloudiskServer::register_userinfo_module()
{
    m_server.GET("/user/info",[](const HttpReq* req,HttpResp* resp){
        user.username = req->query("username");
        const string& token = req->query("token");

        if( !CryptoUtil::verify_token(token,user) )
        {
            resp->set_status(HttpStatusBadRequest);
            resp->String("<html>400 Bad Request!</html>");
            resp->String("Token 无效或过期！");
            return;
        }
        else
        {
            string sql="SELECT created_at FROM tbl_user where username = '" + user.username + "';";

            #if DEBUG
            cout << "sql= " << sql << endl;
            #endif

            resp->MySQL("mysql://root:123@localhost/test",sql,[resp](MySQLResultCursor* cursor){
                if( cursor->get_cursor_status() != MYSQL_STATUS_GET_RESULT )
                {
                    resp->set_status(HttpStatusBadRequest);
                    resp->String("<html>400 Bad Request!</html>");
                    return;
                }
                else
                {
                    vector<MySQLCell> record;
                    string create_time;
                    if( cursor->fetch_row(record) )
                    {
                        create_time=record[0].as_string();
                        json data;
                        data["data"]["Username"] = user.username;
                        data["data"]["SignupAt"] = create_time;

                        #if DEBUG
                        cout << "username= " << user.username << endl;
                        cout << "create_time= " << create_time << endl;
                        #endif

                        resp->String( data.dump() );
                    }
                    else
                    {
                        resp->set_status_code("500");
                        resp->String("Server Internal Error");
                    }
                }
            });
        }

    });
}

void CloudiskServer::register_filelist_module()
{
    m_server.POST("/file/query",[](const HttpReq* req,HttpResp* resp){
        FileInfo file;
        if( req->content_type() != APPLICATION_URLENCODED )
        {
            resp->set_status(HttpStatusBadRequest);
            resp->String("<html>400 Bad Request!</html>");
            return;
        }

        user.username = req->query("username");
        const string& token = req->query("token");
        auto& dataForm=req->form_kv();
        const string& limit = dataForm["limit"];

        #if DEBUG
        cout << "username= " << user.username << endl;
        cout << "limit= " << limit << endl;
        #endif

        if( !CryptoUtil::verify_token(token,user) )
        {
            resp->set_status(HttpStatusBadRequest);
            resp->String("<html>400 Bad Request!</html>");
            resp->String("Token 无效或过期！");
            return;
        }
        else
        {
            cout << "user.id= " << user.id << endl;
            string sql="SELECT id FROM tbl_user where username = '" + user.username + "';";
            
            #if DEBUG
            cout << "sql= " << sql << endl;
            #endif

            resp->MySQL("mysql://root:123@localhost/test",sql,[&file,resp,limit](MySQLResultCursor* cursor)
            {
            if( cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT )
            {
                vector<MySQLCell> record;
                if( cursor->fetch_row(record))
                {
                    if (record.empty())
                    {
                        Json result;
                        resp->String( result.dump() );
                        return;
                    }
                    file.uid = to_string(record[0].as_int());
                }

                string sql1="SELECT * FROM tbl_file where uid = '" + file.uid + "'"
                + " LIMIT " + limit + ";";
                #if DEBUG
                cout << "sql1= " << sql1 << endl;
                #endif
                resp->MySQL("mysql://root:123@localhost/test",sql1,[resp,limit](MySQLResultCursor* cursor){
                vector<MySQLCell> record;
                int i=0;
                if( cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT )
                {
                    json result = json::array();
                    while( cursor->fetch_row(record) )
                    {
                        if (record.empty()) continue;
                        json fileObj;
                        fileObj["FileHash"] = record[3].as_string();
                        fileObj["FileName"] = record[2].as_string();
                        fileObj["FileSize"] = record[4].as_ulonglong();
                        fileObj["UploadAt"] = record[5].as_string();
                        fileObj["LastUpdated"] = record[6].as_string();
                        result.push_back(fileObj);
                    }
                    resp->String( result.dump() );
                }
                else
                {
                    resp->set_status(HttpStatusBadRequest);
                    resp->String("<html>400 Bad Request!</html>");
                    resp->String("mysql task error!");
                    return;
                }
            });
            }
            else
            {
                resp->set_status(HttpStatusBadRequest);
                resp->String("<html>400 Bad Request!</html>");
                resp->String("Token 无效或过期！");
                return;
            }
        }); 
        } 
    });
}

void CloudiskServer::register_fileupload_module()
{
    m_server.POST("/file/upload",[](const HttpReq* req,HttpResp* resp,SeriesWork* series){
        user.username = req->query("username");
        const string& token = req->query("token");

        #if DEBUG
        cout << "username= " << user.username << endl;
        cout << "token= " << token << endl;
        #endif

        const Form& formData = req->form();
        string fileContent;
        for (const auto& [_, fileInfo]: formData) 
        {
            const auto& [filename, content] = fileInfo;
            file.filename = filename;
            fileContent = content;
            file.size = to_string(fileContent.size());
        }

        //容灾备份，内存上传
        if (CloudMoniter::uploadFile(file.filename,fileContent) == -1 )
        {
            cout << "CloudMoniter failed!" << endl;
        }
        else
        {
            cout << "CloudMoniter success!" << endl;
        }
        #if DEBUG
        cout << "filename= " << file.filename << endl;
        cout << "fileContent= " << fileContent << endl;
        cout << "filesize= " << file.size << endl;
        #endif

        if( !CryptoUtil::verify_token(token,user) )
        {
            resp->set_status(HttpStatusBadRequest);
            resp->String("<html>400 Bad Request!</html>");
            resp->String("Token 无效或过期！");
            return;
        }

        cout << "user.uid= " << user.id << endl;
        string salt = CryptoUtil::generate_salt(8);
        file.hashcode = CryptoUtil::generate_hash(fileContent,salt);

        #if DEBUG
        cout << "salt= " << salt << endl;
        cout << "hashcode= " << file.hashcode << endl;
        #endif

        if( req->content_type() != MULTIPART_FORM_DATA )
        {
            resp->set_status(HttpStatusBadRequest);
            resp->String("<html>400 Bad Request!</html>");
            resp->String("文件上传格式错误，请用form-data方式");
            return;
        }

        const Form& form=req->form();
        for( const auto& [_,fileInfo]: form)
        {
            const auto&[filename,content] = fileInfo;
            #if DEBUG
            cout << filename << ": " << content << endl;
            #endif
            string path = "./resource/" + file.hashcode;
            resp->Save(path,std::move(content));
        }
        
        string sql="SELECT id FROM tbl_user where username = '" + user.username + "';";
        resp->MySQL("mysql://root:123@localhost/test",sql,[fileContent,resp](MySQLResultCursor* cursor){
            if( cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT )
            {
                vector<MySQLCell> record;
                if( cursor->fetch_row(record) )
                {
                    file.uid = to_string(record[0].as_int());
                    cout << "file.uid= " << file.uid << endl;
                    string sql1="INSERT INTO tbl_file (uid,filename,hashcode,size) VALUES('" + file.uid 
                     + "' , '" + file.filename + "','" + file.hashcode + "','" + file.size + "');";

                    #if DEBUG
                    cout << "[SQL1]= " << sql1 << endl;
                    #endif
                    resp->MySQL("mysql://root:123@localhost/test",sql1,[resp](MySQLResultCursor* cursor){
                    if( cursor->get_cursor_status() == MYSQL_STATUS_OK )
                    {
                        resp->set_status(HttpStatusFound);
                        resp->headers["Location"] = "/static/view/home.html";
                    }
                    else
                    {
                        resp->set_status(HttpStatusBadRequest);
                        resp->String("<html>400 Bad Request!</html>");
                    }
                });
                }
            }
            else
            {
                resp->set_status(HttpStatusBadRequest);
                resp->String("<html>400 Bad Request!</html>");
                resp->String("Token 无效或过期！");
                return;
            }
        }); 
    });
}

void CloudiskServer::register_filedownload_module()
{
    m_server.GET("/file/download",[](const HttpReq* req,HttpResp* resp){
        file.filename = req->query("filename");
        file.hashcode = req->query("filehash");
        user.username = req->query("username");
        string token = req->query("token");

        #if DEBUG
        cout << "filename= " << file.filename << endl;
        cout << "filehash= " << file.hashcode << endl;
        cout << "user.username= " << user.username << endl;
        cout << "token= " << token << endl;
        #endif

        if( !CryptoUtil::verify_token(token,user) )
        {
            resp->set_status(HttpStatusBadRequest);
            resp->String("<html>400 Bad Request!</html>");
            resp->String("Token 无效或过期！");
            return;
        }
        
        string sql = "SELECT status FROM tbl_file where filename = '" + file.filename + "';";

        #if DEBUG
        cout << "sql= " << sql << endl;
        #endif

        resp->MySQL("mysql://root:123@localhost/test",sql,[resp,req](MySQLResultCursor* cursor){
            int status;
            if( cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT )
            {
                vector<MySQLCell> record;
                if( cursor->fetch_row( record ))
                {
                    status = record[0].as_int();
                }
            }
            else
            {
                resp->set_status(HttpStatusBadRequest);
                resp->String("<html>400 Bad Request!</html>");
                return;
            }
            if( status == 0)
            {
                // 设置标头
                string path = "resource/" + file.hashcode;
                #ifdef DEBUG
                cout << path << endl;
                cout << "filename: " << file.filename << endl;  
                #endif
                resp->add_header_pair("Content-Disposition", "attachment; filename=" + file.hashcode);
                resp->File(path);
            }
            else
            {
                resp->set_status(HttpStatusBadRequest);
                resp->String("<html>400 Bad Request!</html>");
                return;
            }
        });
      
    });
}